 package org.springframework.remoting.client;
 
 import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Set;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
import org.springframework.remoting.service.ServiceType;
import org.springframework.remoting.service.annotation.RemoteService;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
 
 public class ClassPathRemoteServiceScanner extends ClassPathBeanDefinitionScanner
 {
   private Class<? extends Annotation> annotationClass;
   private String baseUrl = null;
   private String rmiIp = null;
   private String httpInvokerRequestExecutorBeanName;
   
   public final Class<? extends Annotation> getAnnotationClass() {
     return this.annotationClass;
   }
   
   public final void setAnnotationClass(Class<? extends Annotation> annotationClass)
   {
     this.annotationClass = annotationClass;
   }
   
   public final String getBaseUrl() {
     return this.baseUrl;
   }
   
   public final void setBaseUrl(String baseUrl) {
     this.baseUrl = baseUrl;
   }
   
   public final String getRmiIp() {
     return this.rmiIp;
   }
   
   public final void setRmiIp(String rmiIp) {
     this.rmiIp = rmiIp;
   }
   
   public final String getHttpInvokerRequestExecutorBeanName() {
     return this.httpInvokerRequestExecutorBeanName;
   }
   
   public final void setHttpInvokerRequestExecutorBeanName(String httpInvokerRequestExecutorBeanName)
   {
     this.httpInvokerRequestExecutorBeanName = httpInvokerRequestExecutorBeanName;
   }
   
   public ClassPathRemoteServiceScanner(BeanDefinitionRegistry registry) {
     super(registry, false);
   }
 
   public void registerFilters()
   {
     if (this.annotationClass != null) {
       addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
     }
     
     addExcludeFilter(new TypeFilter()
     {
       public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
         throws IOException
       {
         String className = metadataReader.getClassMetadata().getClassName();
         return className.endsWith("package-info");
       }
     });
   }
 
   public Set<BeanDefinitionHolder> doScan(String... basePackages)
   {
     Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
     
     for (BeanDefinitionHolder holder : beanDefinitions)
     {
       GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
       if (this.logger.isDebugEnabled()) {
         this.logger.debug("Creating HttpInvokerProxyFactoryBean with name '" + holder
           .getBeanName() + "' and '" + definition
           .getBeanClassName() + "' serviceInterface");
       }
       
       definition.getPropertyValues().add("serviceInterface", definition
         .getBeanClassName());
       RemoteService remoteService = null;
       try {
         Class<?> clazz = ClassUtils.forName(definition.getBeanClassName(), ClassUtils.getDefaultClassLoader());
         remoteService = (RemoteService)AnnotationUtils.findAnnotation(clazz, RemoteService.class);
       } catch (Exception e) {
         this.logger.error(e.getMessage(), e);
       }
       
       if (remoteService == null) {
         definition.setBeanClass(HttpInvokerProxyFactoryBean.class);
       }
       if (this.logger.isDebugEnabled()) {
         this.logger.debug("RemoteService serviceType = " + remoteService.serviceType());
       }
       if ((ServiceType.HTTP == remoteService.serviceType()) && 
         (StringUtils.hasText(this.httpInvokerRequestExecutorBeanName))) {
         definition.getPropertyValues().add("httpInvokerRequestExecutor", new RuntimeBeanReference(this.httpInvokerRequestExecutorBeanName));
       }
 
       if (ServiceType.RMI == remoteService.serviceType()) {
         definition.getPropertyValues().add("serviceUrl", "rmi://" + 
           getRmiIp() + ":" + (remoteService == null ? 1090 : remoteService.registryPort()) + "/" + definition.getBeanClassName());
       } else {
         definition.getPropertyValues().add("serviceUrl", 
           getBaseUrl() + definition.getBeanClassName());
       }
       definition.setBeanClass(remoteService.serviceType().getProxyFactoryBean());
       definition.setAutowireMode(2);
     }
     return beanDefinitions;
   }
   
 
 
 
 
 
   protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition)
   {
     return (beanDefinition.getMetadata().isInterface()) && (beanDefinition.getMetadata().isIndependent());
   }
   
 
 
 
   protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition)
     throws IllegalStateException
   {
     if (super.checkCandidate(beanName, beanDefinition)) {
       return true;
     }
     this.logger.warn("Skipping HttpInvokerProxyFactoryBean with name '" + beanName + "' and '" + beanDefinition
       .getBeanClassName() + "' serviceInterface" + ". Bean already defined with the same name!");
     
 
     return false;
   }
 }